<template>
  <view class="chat-page">
    <nav-bar
      title="博客聊天室"
      title-color="#2f3a65"
      icon-color="#2f3a65"
      :show-home="true"
      :show-back="true"
      background="linear-gradient(90deg, #eef2ff 0%, #f8f9ff 100%)"
    />
    <view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>

    <view class="chat-content">
      <scroll-view
        id="messageScroll"
        class="message-scroll"
        scroll-y
        :scroll-with-animation="true"
        :scroll-top="scrollTop"
        :scroll-into-view="scrollIntoView"
        :style="'height:' + scrollViewHeight + 'px'"
      >
        <view v-if="!messages.length && loadStatus === 'loading'" class="empty">
          加载中...
        </view>
        <view v-else-if="!messages.length" class="empty">
          暂无聊天记录，快来打个招呼吧～
        </view>

        <view
          v-for="msg in messages"
          :key="msg.localKey"
          :id="'msg-' + msg.id"
          class="message-row"
          :class="{ self: msg.userId === userInfo.id }"
        >
          <image class="avatar" :src="msg.avatar" mode="aspectFill" @longpress="handleAvatarLongPress(msg)" />
          <view class="bubble" @longpress="handleMessageLongPress(msg)">
            <view class="meta">
              <view class="name">{{ msg.name }}</view>
              <view class="meta-extra">
                <text class="time">{{ msg.displayTime }}</text>
                <text class="location" v-if="msg.location">{{ parseLocation(msg.location) }}</text>
              </view>
            </view>

            <view class="recalled" v-if="msg.isRecalled">消息已撤回</view>

            <block v-else>
              <view
                v-if="msg.replyContent"
                class="reply"
                @click="scrollToMessage(msg.replyId)"
              >
                <view class="reply-header">
                  <view class="reply-indicator"></view>
                  <text class="reply-user">@{{ msg.replyUserName || 'TA' }}</text>
                </view>
                <mp-html
                  class="reply-html"
                  :content="msg.replyHtml"
                />
              </view>

              <mp-html
                v-if="msg.type === 'text'"
                class="message-html"
                :content="msg.textHtml"
                :tag-style="tagStyles"
              />

              <image
                v-else-if="msg.type === 'image'"
                class="message-image"
                :src="msg.content"
                mode="widthFix"
                @click="previewImage(msg.content)"
              />

              <view
                v-else-if="msg.type === 'file'"
                class="file-message"
                @click="downloadFile(msg)"
              >
                <u-icon name="file-text" size="34" color="#4f6ef7" />
                <view class="file-info">
                  <text class="file-name">{{ msg.fileName || '文件' }}</text>
                  <text class="file-size">{{ msg.fileSize || '' }}</text>
                </view>
              </view>

              <view v-else-if="msg.type === 'audio'" class="audio-message">
                <u-icon name="play-circle" size="36" color="#4f6ef7" />
                <text class="audio-duration">
                  {{ msg.duration ? msg.duration + '秒' : '语音' }}
                </text>
              </view>
            </block>
          </view>
        </view>

        <view :id="bottomAnchorId" style="height: 1rpx;"></view>
      </scroll-view>
    </view>

    <view class="input-area">
      <textarea
        class="message-input"
        v-model="messageText"
        :maxlength="500"
        placeholder="输入消息，与大家畅聊..."
        auto-height
        confirm-type="send"
        :disabled="isSending"
        @confirm="sendMessage"
      />

      <view class="input-actions">
        <u-icon
          name="photo"
          size="42"
          color="#4f6ef7"
          class="action-icon"
          @click="chooseImage"
        />
        <button
          class="send-btn"
          :class="{ disabled: disableSend }"
          :disabled="disableSend"
          @click="sendMessage"
        >
          <text v-if="countdown > 0">{{ countdown }}s</text>
          <text v-else>发送</text>
        </button>
      </view>
    </view>

  </view>
</template>

<script>
import { getChatMsgListApi, sendChatMsgApi, recallChatMsgApi } from '@/api/chat'
import { formatTime } from '@/utils/time'
import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
import { baseUrl, wsBaseUrl } from '@/utils/config'
import { marked } from 'marked'
import hljs from 'highlight.js/lib/common'
import 'highlight.js/styles/atom-one-dark.css'

marked.setOptions({
  breaks: true,
  gfm: true,
  highlight(code, language) {
    if (language && hljs.getLanguage(language)) {
      return hljs.highlight(code, { language }).value
    }
    return hljs.highlightAuto(code).value
  }
})

export default {
  components: {
    mpHtml
  },
  data() {
    return {
      userInfo: {},
      messages: [],
      navHeight: 88,
      params: {
        pageNum: 1,
        pageSize: 20
      },
      hasMore: true,
      loadStatus: 'loading',
      messageText: '',
      countdown: 0,
      countdownTimer: null,
      scrollIntoView: '',
      scrollTop: 0,
      bottomAnchorId: 'bottom-anchor',
      scrollViewHeight: 0,
      socketTask: null,
      heartbeatTimer: null,
      reconnectTimer: null,
      reconnectAttempts: 0,
      maxReconnectAttempts: 5,
      shouldReconnect: true,
      isSending: false,
      socketListeners: {
        open: null,
        message: null,
        close: null,
        error: null
      },
      tagStyles: {
        mention: 'color:#4f6ef7;font-weight:500;padding:0 6rpx;background:#eef1ff;border-radius:8rpx;',
        pre: 'background:#1e1e1e;color:#e8e8e8;padding:16rpx;border-radius:12rpx;overflow:auto;',
        code: 'color:#ffcb6b;'
      }
    }
  },
  computed: {
    disableSend() {
      return (!this.messageText.trim() || this.isSending || this.countdown > 0)
    }
  },
  onLoad() {
    this.initLayout()
    this.userInfo = uni.getStorageSync('userInfo') || {}
    const token = uni.getStorageSync('token')

    if (!token || !this.userInfo?.id) {
      uni.showModal({
        title: '提示',
        content: '登录后即可加入聊天室，是否前往登录？',
        confirmText: '去登录',
        success: (res) => {
          if (res.confirm) {
            uni.switchTab({ url: '/pages/mine/index' })
          } else {
            uni.navigateBack()
          }
        }
      })
      return
    }

    this.initChat()
  },
  onReady() {
    this.setScrollViewHeight()
    this.retryScrollToBottom()
  },
  onShow() {
    const latestUser = uni.getStorageSync('userInfo') || {}
    if (latestUser.id !== this.userInfo.id) {
      this.userInfo = latestUser
    }
    setTimeout(() => {
      this.setScrollViewHeight()
      this.scrollToBottom()
    }, 200)
  },
  onUnload() {
    this.cleanup()
  },
  created() {
    this.$watch(() => this.messages.length, () => {
      this.retryScrollToBottom()
    })
  },
  methods: {
    async initChat() {
      await this.fetchMessages(true)
      this.connectSocket()
      this.retryScrollToBottom()
    },
    async fetchMessages(reset = false) {
      if (reset) {
        this.params.pageNum = 1
        this.hasMore = true
        this.messages = []
      }

      this.loadStatus = 'loading'
      try {
        const res = await getChatMsgListApi(this.params)
        const records = (res.data && res.data.records) || []
        const normalized = records.map(this.normalizeMessage).reverse()

        if (reset) {
          this.messages = normalized
        } else {
          this.messages = [...normalized, ...this.messages]
        }

        if (records.length < this.params.pageSize) {
          this.hasMore = false
          this.loadStatus = 'nomore'
        } else {
          this.params.pageNum += 1
          this.loadStatus = 'loadmore'
        }

        this.$nextTick(() => this.retryScrollToBottom())
      } catch (e) {
        this.loadStatus = 'loadmore'
        uni.showToast({ title: '加载聊天记录失败', icon: 'none' })
        console.error(e)
      }
    },
    connectSocket() {
      if (!this.userInfo?.id) return
      this.closeSocket()
      this.shouldReconnect = true

      const token = uni.getStorageSync('token') || ''
      const url = `${wsBaseUrl}${this.userInfo.id}?t=${Date.now()}${token ? `&token=${encodeURIComponent(token)}` : ''}`
      this.socketTask = uni.connectSocket({
        url,
        header: token ? { Authorization: token } : {}
      })

      if (!this.socketTask) {
        this.bindGlobalSocketEvents()
        return
      }

      if (typeof this.socketTask.onOpen === 'function') {
        this.socketTask.onOpen(() => {
          this.reconnectAttempts = 0
          this.startHeartbeat()
          this.$nextTick(() => this.retryScrollToBottom())
        })
        this.socketTask.onMessage((event) => this.handleSocketMessage(event))
        this.socketTask.onClose(() => {
          this.clearHeartbeat()
          if (this.shouldReconnect) this.tryReconnect()
        })
        this.socketTask.onError(() => {
          this.clearHeartbeat()
          if (this.shouldReconnect) this.tryReconnect()
        })
      } else {
        this.bindGlobalSocketEvents()
      }
    },
    bindGlobalSocketEvents() {
      this.unbindGlobalSocketEvents()
      this.socketListeners.open = () => {
        this.reconnectAttempts = 0
        this.startHeartbeat()
      }
      this.socketListeners.message = (event) => this.handleSocketMessage(event)
      this.socketListeners.close = () => {
        this.clearHeartbeat()
        if (this.shouldReconnect) this.tryReconnect()
      }
      this.socketListeners.error = () => {
        this.clearHeartbeat()
        if (this.shouldReconnect) this.tryReconnect()
      }
      if (typeof uni.onSocketOpen === 'function') uni.onSocketOpen(this.socketListeners.open)
      if (typeof uni.onSocketMessage === 'function') uni.onSocketMessage(this.socketListeners.message)
      if (typeof uni.onSocketClose === 'function') uni.onSocketClose(this.socketListeners.close)
      if (typeof uni.onSocketError === 'function') uni.onSocketError(this.socketListeners.error)
    },
    unbindGlobalSocketEvents() {
      if (this.socketListeners.open && typeof uni.offSocketOpen === 'function') uni.offSocketOpen(this.socketListeners.open)
      if (this.socketListeners.message && typeof uni.offSocketMessage === 'function') uni.offSocketMessage(this.socketListeners.message)
      if (this.socketListeners.close && typeof uni.offSocketClose === 'function') uni.offSocketClose(this.socketListeners.close)
      if (this.socketListeners.error && typeof uni.offSocketError === 'function') uni.offSocketError(this.socketListeners.error)
      this.socketListeners = { open: null, message: null, close: null, error: null }
    },
    handleSocketMessage(event) {
      if (!event || !event.data) return
      let message = event.data
      try {
        if (typeof message === 'string') message = JSON.parse(message)
      } catch {
        return
      }
      if (message.type === 'ping') return

      if (message.isRecalled) {
        const idx = this.messages.findIndex((item) => item.id === message.id)
        if (idx !== -1) {
          this.$set(this.messages[idx], 'isRecalled', true)
          this.$set(this.messages[idx], 'content', message.content)
        }
        return
      }
      const normalized = this.normalizeMessage(message)
      this.messages.push(normalized)
      this.$nextTick(() => this.retryScrollToBottom())
    },
    startHeartbeat() {
      this.clearHeartbeat()
      this.heartbeatTimer = setInterval(() => {
        const payload = JSON.stringify({ type: 'ping', timestamp: Date.now() })
        if (this.socketTask && typeof this.socketTask.send === 'function') {
          this.socketTask.send({ data: payload })
        } else {
          uni.sendSocketMessage({ data: payload })
        }
      }, 30000)
    },
    clearHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer)
        this.heartbeatTimer = null
      }
    },
    tryReconnect() {
      if (!this.shouldReconnect) return
      if (this.reconnectAttempts >= this.maxReconnectAttempts) {
        uni.showToast({ title: '聊天室连接异常', icon: 'none' })
        return
      }
      this.reconnectAttempts += 1
      const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000)
      this.reconnectTimer = setTimeout(() => this.connectSocket(), delay)
    },
    closeSocket() {
      this.unbindGlobalSocketEvents()
      if (this.socketTask) {
        try {
          if (typeof this.socketTask.close === 'function') this.socketTask.close()
          else uni.closeSocket()
        } catch {}
      }
      this.socketTask = null
      this.clearHeartbeat()
      if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer)
        this.reconnectTimer = null
      }
    },
    normalizeMessage(msg) {
      const id = msg.id || `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
      const isText = (msg.type || 'text') === 'text'
      const content = msg.content || ''
      const textHtml = isText ? this.renderMarkdown(this.decorateMentions(content)) : ''
      return {
        ...msg,
        id,
        localKey: `msg-${id}`,
        displayTime: formatTime(msg.time || Date.now()),
        content,
        textHtml,
        replyHtml: msg.replyContent ? this.renderMarkdown(this.decorateMentions(msg.replyContent)) : ''
      }
    },
    renderMarkdown(content) {
      if (!content) return ''
      return marked.parse(content)
    },
    decorateMentions(text) {
      if (!text) return ''
      return text.replace(/\B@([^\s@]+)(?=\s|$)/g, '<mention>@$1</mention>')
    },
    scrollToBottom() {
      if (!this.messages || !this.messages.length) return
      const target = this.bottomAnchorId
      this.scrollIntoView = target
      setTimeout(() => {
        this.scrollIntoView = target
      }, 80)
      this.scrollTop = 10000000
      setTimeout(() => {
        this.scrollTop = this.scrollTop + 1
      }, 100)
    },
    retryScrollToBottom() {
      const attempts = [0, 60, 120, 200, 320]
      attempts.forEach(delay => {
        setTimeout(() => this.scrollToBottom(), delay)
      })
    },
    setScrollViewHeight() {
      try {
        const systemInfo = uni.getSystemInfoSync()
        const windowH = systemInfo.windowHeight || 0
        uni.createSelectorQuery()
          .in(this)
          .select('.input-area')
          .boundingClientRect((rect) => {
            const inputH = (rect && rect.height) ? rect.height : 120
            const navH = this.navHeight || 88
            const h = Math.max(0, windowH - navH - inputH - 8)
            this.scrollViewHeight = h
          })
          .exec()
      } catch {
        this.scrollViewHeight = 500
      }
    },
    sendMessage() {
      if (this.disableSend) return
      const content = this.messageText.trim()
      if (!content) return
      this.isSending = true
      sendChatMsgApi({
        type: 'text',
        content,
        name: this.userInfo.nickname,
        userId: this.userInfo.id,
        avatar: this.userInfo.avatar,
        sex: this.userInfo.sex || null
      }).then(() => {
        this.messageText = ''
        this.startCountdown()
      }).catch(() => {
        uni.showToast({ title: '发送失败，请重试', icon: 'none' })
      }).finally(() => {
        this.isSending = false
      })
    },
    startCountdown() {
      this.countdown = 3
      if (this.countdownTimer) clearInterval(this.countdownTimer)
      this.countdownTimer = setInterval(() => {
        if (this.countdown > 0) this.countdown -= 1
        else {
          clearInterval(this.countdownTimer)
          this.countdownTimer = null
        }
      }, 1000)
    },
    chooseImage() {
      if (this.countdown > 0 || this.isSending) return
      uni.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        success: (chooseRes) => {
          if (!chooseRes.tempFilePaths.length) return
          const filePath = chooseRes.tempFilePaths[0]
          uni.showLoading({ title: '上传中...', mask: true })
          uni.uploadFile({
            url: `${baseUrl}/file/upload?source=chat`,
            filePath,
            name: 'file',
            header: { Authorization: uni.getStorageSync('token') || '' },
            success: async (uploadRes) => {
              try {
                const data = JSON.parse(uploadRes.data || '{}')
                if (data.code !== 200 || !data.data) throw new Error(data.msg || '上传失败')
                await sendChatMsgApi({
                  type: 'image',
                  content: data.data,
                  name: this.userInfo.nickname,
                  userId: this.userInfo.id,
                  avatar: this.userInfo.avatar,
                  sex: this.userInfo.sex || null
                })
                this.startCountdown()
              } catch (err) {
                uni.showToast({ title: err?.message || '上传失败，请重试', icon: 'none' })
              }
            },
            fail: () => {
              uni.showToast({ title: '上传失败，请重试', icon: 'none' })
            },
            complete: () => uni.hideLoading()
          })
        }
      })
    },
    previewImage(url) {
      uni.previewImage({ urls: [url], current: url })
    },
    downloadFile(msg) {
      if (!msg.content) return
      uni.showLoading({ title: '下载中...', mask: true })
      uni.downloadFile({
        url: msg.content,
        success: (res) => {
          if (res.statusCode === 200) {
            uni.openDocument({ filePath: res.tempFilePath, showMenu: true })
          } else {
            uni.showToast({ title: '下载失败，请稍后重试', icon: 'none' })
          }
        },
        fail: () => uni.showToast({ title: '下载失败', icon: 'none' }),
        complete: () => uni.hideLoading()
      })
    },
    parseLocation(location) {
      if (!location) return ''
      const parts = location.split('|').filter(Boolean)
      if (parts.length <= 1) return parts[0] || ''
      return parts.slice(1).join('·')
    },
    handleMessageLongPress(msg) {
      if (!msg || msg.userId !== this.userInfo.id || msg.isRecalled) return
      uni.showActionSheet({
        itemList: ['撤回消息'],
        success: async () => {
          try {
            await recallChatMsgApi({ id: msg.id })
          } catch {
            uni.showToast({ title: '撤回失败', icon: 'none' })
          }
        }
      })
    },
    initLayout() {
      try {
        const systemInfo = uni.getSystemInfoSync()
        this.navHeight = (systemInfo.statusBarHeight || 20) + 44
      } catch {
        this.navHeight = 88
      }
    },
    handleAvatarLongPress(msg) {
      if (!msg?.name) return
      const mentionName = msg.name
      const needsSpace = this.messageText && !/\s$/.test(this.messageText)
      this.messageText = `${this.messageText || ''}${needsSpace ? ' ' : ''}@${mentionName} `
    },
    cleanup() {
      this.shouldReconnect = false
      this.closeSocket()
      if (this.countdownTimer) {
        clearInterval(this.countdownTimer)
        this.countdownTimer = null
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.chat-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, #f8f9ff 0%, #ffffff 100%);
  padding-bottom: env(safe-area-inset-bottom);
}

.nav-placeholder {
  width: 100%;
}

.chat-content {
  flex: 1;
  padding: 0 24rpx 220rpx;
  box-sizing: border-box;
}

.message-scroll {
  padding-bottom: 16rpx;
  box-sizing: border-box;
}

.empty {
  text-align: center;
  color: #999;
  padding: 80rpx 0;
  font-size: 28rpx;
}

.message-row {
  display: flex;
  align-items: flex-start;
  margin-bottom: 28rpx;

  &.self {
    flex-direction: row-reverse;

    .bubble {
      background: #4f6ef7;
      color: #fff;
      border-top-right-radius: 8rpx;
      border-top-left-radius: 28rpx;
      align-items: flex-end;

      .meta .name {
        color: #e8eaff;
      }

      .meta-extra .time,
      .meta-extra .location {
        color: rgba(255, 255, 255, 0.7);
      }

      mp-html {
        color: #fff;
      }

      .reply {
        background: rgba(255, 255, 255, 0.18);
      }
    }
  }
}

.avatar {
  width: 72rpx;
  height: 72rpx;
  border-radius: 50%;
  margin: 0 16rpx;
  box-shadow: 0 6rpx 18rpx rgba(79, 110, 247, 0.12);
}

.bubble {
  max-width: 72%;
  background: #fff;
  border-radius: 28rpx;
  padding: 24rpx 28rpx;
  box-shadow: 0 10rpx 30rpx rgba(36, 56, 128, 0.08);
  display: flex;
  flex-direction: column;
  gap: 12rpx;
  position: relative;
  word-break: break-word;

  .message-html {
    font-size: 28rpx;
    color: #2f3a65;
    line-height: 1.6;

    ::v-deep p {
      margin: 0;
    }

    ::v-deep pre {
      margin: 16rpx 0 0;
      position: relative;
      background: #282c34;
      color: #f6f8fa;
      border-radius: 14rpx;
      padding: 36rpx 24rpx 24rpx;
      overflow: auto;
      box-shadow: 0 12rpx 32rpx rgba(40, 44, 52, 0.35);
    }

    ::v-deep pre::before {
      content: '';
      position: absolute;
      top: 12rpx;
      left: 24rpx;
      display: flex;
      gap: 12rpx;
      width: 60rpx;
      height: 10rpx;
      border-radius: 6rpx;
      background: radial-gradient(circle at 10% 50%, #ff5f56 0, #ff5f56 35%, transparent 40%), radial-gradient(circle at 50% 50%, #ffbd2e 0, #ffbd2e 35%, transparent 40%), radial-gradient(circle at 90% 50%, #27c93f 0, #27c93f 35%, transparent 40%);
      background-repeat: no-repeat;
      background-size: 12rpx 12rpx;
    }

    ::v-deep code {
      font-size: 24rpx;
    }

    ::v-deep :not(pre) > code {
      background: rgba(79, 110, 247, 0.12);
      color: #3146c6;
      padding: 4rpx 10rpx;
      border-radius: 10rpx;
      margin: 0 4rpx;
    }

    ::v-deep a {
      color: #4f6ef7;
      text-decoration: underline;
    }
  }
}

.meta {
  display: flex;
  flex-direction: column;
  gap: 8rpx;

  .name {
    font-size: 30rpx;
    font-weight: 600;
    color: #2f3a65;
  }
}

.meta-extra {
  display: flex;
  align-items: center;
  gap: 16rpx;
  font-size: 24rpx;
  color: #9095b6;
}

.location {
  display: flex;
  align-items: center;
}

.recalled {
  font-size: 26rpx;
  color: #9095b6;
  font-style: italic;
}

.reply {
  position: relative;
  background: linear-gradient(135deg, rgba(79, 110, 247, 0.12), rgba(116, 140, 255, 0.18));
  border-radius: 18rpx;
  padding: 20rpx 24rpx 24rpx 28rpx;
  display: flex;
  flex-direction: column;
  gap: 12rpx;
  font-size: 24rpx;
  color: #39437a;
  box-shadow: inset 0 0 0 1rpx rgba(79, 110, 247, 0.12), 0 12rpx 24rpx rgba(79, 110, 247, 0.12);
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 8rpx;
    background: linear-gradient(180deg, rgba(79, 110, 247, 0.85), rgba(153, 171, 255, 0.9));
    border-top-left-radius: 18rpx;
    border-bottom-left-radius: 18rpx;
  }

  .reply-header {
    display: flex;
    align-items: center;
    gap: 12rpx;

    .reply-indicator {
      width: 18rpx;
      height: 18rpx;
      border-radius: 50%;
      background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.4));
      box-shadow: 0 4rpx 12rpx rgba(79, 110, 247, 0.35);
    }

    .reply-user {
      font-weight: 600;
      font-size: 26rpx;
      color: #3146c6;
      letter-spacing: 1rpx;
    }
  }

  .reply-html {
    font-size: 26rpx;
    color: #2f3a65;
    line-height: 1.5;

    ::v-deep p {
      margin: 0;
    }

    ::v-deep pre {
      margin: 16rpx 0 0;
      position: relative;
      background: #282c34;
      color: #f6f8fa;
      border-radius: 14rpx;
      padding: 36rpx 24rpx 24rpx;
      overflow: auto;
      box-shadow: 0 12rpx 32rpx rgba(40, 44, 52, 0.35);
    }

    ::v-deep pre::before {
      content: '';
      position: absolute;
      top: 12rpx;
      left: 24rpx;
      display: flex;
      gap: 12rpx;
      width: 60rpx;
      height: 10rpx;
      border-radius: 6rpx;
      background: radial-gradient(circle at 10% 50%, #ff5f56 0, #ff5f56 35%, transparent 40%), radial-gradient(circle at 50% 50%, #ffbd2e 0, #ffbd2e 35%, transparent 40%), radial-gradient(circle at 90% 50%, #27c93f 0, #27c93f 35%, transparent 40%);
      background-repeat: no-repeat;
      background-size: 12rpx 12rpx;
    }

    ::v-deep code {
      font-size: 24rpx;
    }

    ::v-deep :not(pre) > code {
      background: rgba(79, 110, 247, 0.16);
      color: #3146c6;
      padding: 4rpx 10rpx;
      border-radius: 10rpx;
      margin: 0 4rpx;
    }

    ::v-deep blockquote {
      margin: 12rpx 0;
      padding: 12rpx 18rpx;
      border-left: 6rpx solid rgba(79, 110, 247, 0.6);
      background: rgba(79, 110, 247, 0.08);
      border-radius: 12rpx;
      color: #3d4c8f;
    }
  }
}

.message-image {
  max-width: 480rpx;
  border-radius: 20rpx;
  box-shadow: 0 10rpx 20rpx rgba(79, 110, 247, 0.15);
}

.file-message {
  display: flex;
  align-items: center;
  gap: 16rpx;
  padding: 18rpx 20rpx;
  border-radius: 20rpx;
  background: rgba(79, 110, 247, 0.08);

  .file-info {
    display: flex;
    flex-direction: column;
    gap: 6rpx;

    .file-name {
      font-size: 28rpx;
      color: #2f3a65;
      font-weight: 500;
    }

    .file-size {
      font-size: 24rpx;
      color: #9aa0c3;
    }
  }
}

.audio-message {
  display: flex;
  align-items: center;
  gap: 12rpx;
  padding: 18rpx 22rpx;
  border-radius: 20rpx;
  background: rgba(79, 110, 247, 0.08);

  .audio-duration {
    font-size: 28rpx;
    color: #2f3a65;
  }
}

.input-area {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 20rpx 28rpx calc(env(safe-area-inset-bottom) + 20rpx);
  background: linear-gradient(180deg, rgba(248, 249, 255, 0.95) 0%, #f8f9ff 100%);
  box-shadow: 0 -8rpx 40rpx rgba(60, 85, 165, 0.12);
  display: flex;
  align-items: flex-end;
  gap: 20rpx;
}

.message-input {
  flex: 1;
  min-height: 90rpx;
  max-height: 220rpx;
  background: #fff;
  border-radius: 26rpx;
  padding: 20rpx 24rpx;
  font-size: 30rpx;
  color: #2f3a65;
  line-height: 1.4;
  box-shadow: inset 0 0 0 1rpx rgba(79, 110, 247, 0.08);
}

.input-actions {
  display: flex;
  align-items: center;
  gap: 20rpx;
}

.action-icon {
  background: #fff;
  padding: 12rpx;
  border-radius: 20rpx;
  box-shadow: 0 6rpx 20rpx rgba(79, 110, 247, 0.2);
}

.send-btn {
  width: 140rpx;
  height: 80rpx;
  border-radius: 26rpx;
  background: linear-gradient(135deg, #4f6ef7 0%, #5f8cff 100%);
  color: #fff;
  font-size: 30rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10rpx 30rpx rgba(79, 110, 247, 0.35);
  border: none;

  &.disabled {
    opacity: 0.6;
    box-shadow: none;
  }
}
</style>


